package com.wolfeyes.common.utils.redis;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wolfeyes.common.utils.serialize.SerializeUtilss;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Set;

/**
 * @descript 定义redis底层实现类RedisUtils
 * @description 操作单个Redis方法封装类-Util工具类
 * @projectName bootdo
 * @className RedisUtils.java
 * @author zhangfp
 * @email 1989zhangfp@163.com
 * @date 2016-06-01 15:53:58
 * @since 2017年02月02日下午5:37:39
 * @createTime 2016年06月01日 下午17:04:13
 * @version 1.0.0
 */
public class RedisUtils {
	
	/**
	 * slf4j日志对象
	 */
    private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);
    
    /**
     * redis.clients.jedis.JedisPool
     * redis client for java
     * 浅译：redis缓存-客户端连接池对象jedisPool
     */
    private JedisPool jedisPool = null;

    public RedisUtils(String host, int port){
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();

        config.setMaxTotal(200);
        config.setMaxIdle(50);
        config.setMinIdle(8);//设置最小空闲数
        config.setMaxWaitMillis(10000);
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
        //Idle时进行连接扫描
        config.setTestWhileIdle(true);
        //表示idle object evitor两次扫描之间要sleep的毫秒数
        config.setTimeBetweenEvictionRunsMillis(30000);
        //表示idle object evitor每次扫描的最多的对象数
        config.setNumTestsPerEvictionRun(10);
        //表示一个对象至少停留在idle状态的最短时间，然后才能被idle object evitor扫描并驱逐；这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
        config.setMinEvictableIdleTimeMillis(60000);
        
		jedisPool = new JedisPool(config, host, port, 10000);
    }

    /**
     * 设置redis存储的key-value值
     * @param key key值
     * @param value value值
     * @param nxxx NX|XX
     *             nxxx的值只能取NX或者XX，如果取NX，则只有当key不存在是才进行set，如果取XX，则只有当key已经存在时才进行set
     * @param expx EX|PX
     *             expx的值只能取EX或者PX，代表数据过期时间的单位，EX代表秒，PX代表毫秒
     * @param time 过期时间，单位是expx所代表的单位
     * @return
     */
    public boolean set(final String key, final String value, final String nxxx,
                       final String expx, final int time) {
    	
        String result = null;
        
        try {
            Jedis redis = jedisPool.getResource();
            SetParams setParams = new SetParams();
            if("ex".equals(expx)) {
            	setParams.ex(time);
            }else {
            	setParams.px(time);
            }
            if("nx".equals(nxxx)) {
            	setParams.nx();
            }else {
            	setParams.xx();
            }
            //result = redis.set(key,value, nxxx,expx,time);
            result = redis.set(key,value, setParams);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	logger.error("redis set [{},{},{},{},{}] error:{}",key,value, nxxx,expx,time,e.getMessage());
        }

		return "OK".equalsIgnoreCase(result) ? true : false;
    }

    /**
     * 设置redis存储的key-value值
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, final String value) {
        String result = null;
        try {
            Jedis redis = this.jedisPool.getResource();
            result = redis.set(key,value);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	logger.error("redis set [{},{}] error:{}",key,value,e.getMessage());
        }

        return "OK".equalsIgnoreCase(result)?true:false;
    }

    /**
     * 设置redis存储的key-value值
     * @param key
     * @param second 单位秒
     * @param value
     * @return
     */
    public boolean setEx(String key,int second,String value) {
        String ret = null;
        try {
            Jedis redis = jedisPool.getResource();
            ret = redis.setex(key, second, value);
            //jedisPool.returnResource(redis);
            redis.close();
            redis.close();
        } catch (Exception e) {
        	logger.error("redis setEx [{},{},{}] error:{}",key,second,value, e.getMessage());
        }

        return "OK".equalsIgnoreCase(ret)?true:false;
    }

    /**
     * 获取redis中key的String值
     * @param key
     * @return
     */
    public String get(String key) {
        String ret = null;
        try {
            Jedis redis = jedisPool.getResource();
            ret = redis.get(key);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	
        	logger.error("redis get [{}] error:{}",key,e.getMessage());
        }
        return ret;
    }

    /**
     * 删除redis的key值的存储
     * @param key
     * @return
     */
    public long del(String key) {
        long ret = -1;
        try {
            Jedis redis = jedisPool.getResource();
            ret = redis.del(key);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	
        	logger.error("redis del [{}] error:{}",key,e.getMessage());
        }
        return ret;
    }

    /**
     * 添加redis中的set集合值
     * @param key
     * @param value
     * @return
     */
    public long sadd(String key,String value) {
        long result = -1;
        try {
            Jedis redis = jedisPool.getResource();
            result = redis.sadd(key, value);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	
        	logger.error("redis sadd [{},{}] error:{}",key,value,e.getMessage());
        }
        return result;
    }

    /**
     * 删除redis中set集合的key值
     * @param key
     * @param value
     * @return
     */
    public long sdel(String key,String value) {
    	
        long result = -1;
        try {
            Jedis redis = jedisPool.getResource();
            result = redis.srem(key, value);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	
        	logger.error("redis sdel [{},{}] error:{}",key,value,e.getMessage());
        }
        return result;
    }

    /**
     * 获取key值对应的所有set集合值
     * @param key
     * @return
     */
    public Set<String> getAllSet(String key) {
    	
        Set<String> result = null;
        try {
            Jedis redis = jedisPool.getResource();
            result = redis.smembers(key);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	
        	logger.error("redis getAllSet [{}] error:{}",key,e.getMessage());
        }
        return result;
    }

    /**
     * 判断redis中是否存在key值
     * @param key
     * @return
     */
    public boolean exist(String key) {
    	
        boolean result = false;
        try {
            Jedis redis = jedisPool.getResource();
            result = redis.exists(key);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	logger.error("redis exist [{}] error:{}" ,key,e.getMessage());
        }
        return result;
    }

    /**
     * 返回成员 member 是否是存储的集合 key的成员
     * @param key
     * @param member
     * @return 如果member元素是集合key的成员，则返回true.如果member元素不是key的成员，或者集合key不存在，则返回false
     */
    public boolean sismember(String key,String member) {
    	
        boolean ret = false;
        try {
            Jedis redis = jedisPool.getResource();
            ret = redis.sismember(key, member);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	logger.error("redis sismember [{},{}] error:{}" ,key,member,e.getMessage());
        }
        return ret;
    }

    /**
     * 存储key值的value对象
     * @param key
     * @param value
     * @return
     */
    public boolean setObject(String key, Object value) {
    	
        String result = null;
        try {
            Jedis jedis = jedisPool.getResource();
            result = jedis.set(key.getBytes(), SerializeUtilss.toBytes(value));
            //jedisPool.returnResource(jedis);
            jedis.close();
        } catch (Exception e) {
        	logger.error("redis setObject [{}] error:{}",key, e.getMessage());
        }

        return "OK".equalsIgnoreCase(result)?true:false;
    }

    /**
     * 缓存对象，second过期时间
     * @param key
     * @param value
     * @param second    过期时间（秒）
     * @return
     */
    public boolean setObjectEx(String key,Object value,int second) {
    	
        String result = null;
        try {
            Jedis jedis = jedisPool.getResource();
            result = jedis.setex(key.getBytes(), second, SerializeUtilss.toBytes(value));
            //jedisPool.returnResource(jedis);
            jedis.close();
        } catch (Exception e) {
        	logger.error("redis setObject [{}] error:{}",key, e.getMessage());
        }

        return "OK".equalsIgnoreCase(result)?true:false;
    }

    /**
     * 获取key值存储在redis中的对象
     * @param clazz
     * @param key
     * @param <T>
     * @return 如果存在，则返回clazz对象；不存在则返回null
     */
    public <T> T getObject(Class<T> clazz, String key) {
    	
        T result = null;
        try {
            Jedis jedis = jedisPool.getResource();
            byte[] data = jedis.get(key.getBytes());
            if(data!= null){
            	result = SerializeUtilss.getObjectFromBytes(clazz, data);
            }
            //jedisPool.returnResource(jedis);
            jedis.close();
        } catch (Exception e) {
        	
        	logger.error("getObject [{}] error:{}", key,e.getMessage());
        }

        return result;
    }

    /**
     * 向存于 key 的列表的尾部插入所有指定的值。如果 key 不存在，那么会创建一个空的列表然后再进行 push 操作。 当 key
     * 保存的不是一个列表，那么会返回一个错误。
     *
     * 可以使用一个命令把多个元素打入队列，只需要在命令后面指定多个参数。元素是从左到右一个接一个从列表尾部插入。 比如命令 RPUSH mylist a
     * b c 会返回一个列表，其第一个元素是 a ，第二个元素是 b ，第三个元素是 c。
     * @param key
     * @param value
     * @return
     */
    public long rpushObject(String key, Object value) {
    	
        long result = -1;
        try {
            Jedis jedis = jedisPool.getResource();
            result = jedis.rpush(key.getBytes(), SerializeUtilss.toBytes(value));
            //jedisPool.returnResource(jedis);
            jedis.close();
        } catch (Exception e) {
        	
        	logger.error("redis lpushObject [{}] error:{}",key, e.getMessage());
        }

        return result;
    }

    /**
     * 将所有指定的值插入到存于 key 的列表的头部。如果 key 不存在，那么在进行 push 操作前会创建一个空列表。 如果 key
     * 对应的值不是一个 list 的话，那么会返回一个错误。
     *
     * 可以使用一个命令把多个元素 push 进入列表，只需在命令末尾加上多个指定的参数。元素是从最左端的到最右端的、一个接一个被插入到 list
     * 的头部。 所以对于这个命令例子 LPUSH mylist a b c，返回的列表是 c 为第一个元素， b 为第二个元素， a 为第三个元素。
     *
     * @param key
     * @param value
     * @return
     */
    public long lpushObject(String key,Object value) {
    	
        long result = -1;
        try {
            Jedis jedis = jedisPool.getResource();
            result = jedis.lpush(key.getBytes(), SerializeUtilss.toBytes(value));
            //jedisPool.returnResource(jedis);
            jedis.close();
        } catch (Exception e) {
        	
        	logger.error("redis lpushObject [{}] error:{}",key, e.getMessage());
        }

        return result;
    }

    /**
     * 移除并且返回 key 对应的 list 的第一个元素。
     * @param clazz
     * @param key
     * @param <T>
     * @return
     */
    public <T> T lpopObject(Class<T> clazz, String key) {
    	
        T result = null;
        try {
            Jedis jedis = jedisPool.getResource();
            byte[] data = jedis.lpop(key.getBytes());
            if(data!= null){
            	result = SerializeUtilss.getObjectFromBytes(clazz, data);
            }
            //jedisPool.returnResource(jedis);
            jedis.close();
        } catch (Exception e) {
        	
        	logger.error("redis lpopObject [{}] error:{}", key,e.getMessage());
        }

        return result;
    }

    /**
     * 返回列表里的元素的索引 index 存储在 key 里面。 下标是从0开始索引的，所以 0 是表示第一个元素， 1 表示第二个元素，并以此类推。
     * 负数索引用于指定从列表尾部开始索引的元素。在这种方法下，-1 表示最后一个元素，-2 表示倒数第二个元素，并以此往前推。
     *
     * 当 key 位置的值不是一个列表的时候，会返回一个error。
     *
     * @param clazz
     * @param key
     * @param <T>
     * @return
     */
    public <T> T getLeftObject(Class<T> clazz,String key) {
    	
        T result = null;
        try {
            Jedis jedis = jedisPool.getResource();
            byte[] data = jedis.lindex(key.getBytes(), 0);
            if (data != null){
            	result = SerializeUtilss.getObjectFromBytes(clazz,data);
            }
            //jedisPool.returnResource(jedis);
            jedis.close();
        }catch (Exception e){
        	logger.error("redis getObject [{}] error:{}",key,e.getMessage());
        }
        return result;
    }

    /**
     * 返回存储在 key 里的list的长度。 如果 key 不存在，那么就被看作是空list，并且返回长度为 0。 当存储在 key
     * 里的值不是一个list的话，会返回error。
     *
     * @param key
     * @return
     */
    public long llen(String key) {
    	
        long result = -1;
        try {
            Jedis jedis = jedisPool.getResource();
            result = jedis.llen(key);
            //jedisPool.returnResource(jedis);
            jedis.close();
        } catch (Exception e) {
        	logger.error("redis llen [{}] error:{}",key, e.getMessage());
        }

        return result;
    }

    /**
     *
     * 为key设置一个特定的过期时间，单位为秒。过期时间一到，redis将会从缓存中删除掉该key。
     * 即使是有过期时间的key，redis也会在持久化时将其写到硬盘中，并把相对过期时间改为绝对的Unix过期时间。
     * 在一个有设置过期时间的key上重复设置过期时间将会覆盖原先设置的过期时间。
     *
     * @param key
     * @param second
     * @return
     */
    public long keyExpire(String key,int second) {
    	
        long result = -1L;
        try {
            Jedis jedis = jedisPool.getResource();
            result = jedis.expire(key, second);
            //jedisPool.returnResource(jedis);
            jedis.close();
        }catch (Exception e){
        	
        	logger.error("redis keyExpire [{}] error:{}",key,e.getLocalizedMessage());
        }
        return result;
    }

    public Long hset(String key, String field, String value) {
    	
        long result = -1;
        try {
            Jedis redis = jedisPool.getResource();
            result = redis.hset(key, field, value);
            //jedisPool.returnResource(redis);
            redis.close();
        }catch (Exception e){
        	
        	logger.error("redis hset [{},{},{}] error:{}",key,field,value,e.getMessage());
        }
        return result;
    }

    public String hget(String key, String field) {
    	
        String result = null;
        try {
            Jedis redis = jedisPool.getResource();
            result = redis.hget(key, field);
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	
        	logger.error("redis hget [{},{}] error:{}",key,field,e.getMessage());
        }
        return result;
    }

    public <T> T hgetObject(Class<T> clazz,String key, String field) {
    	
        T result = null;
        try {
            Jedis redis = jedisPool.getResource();
            byte[] data = redis.hget(key.getBytes(), field.getBytes());
            if (data != null){
            	result = SerializeUtilss.getObjectFromBytes(clazz,data);
            }
            //jedisPool.returnResource(redis);
            redis.close();
        } catch (Exception e) {
        	
        	logger.error("redis hgetObject [{},{}] error:{}",key,field,e.getMessage());
        }
        return result;
    }
    
}

